package mybatis.plugins;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.ibatis.binding.MapperRegistry;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * 打印mapper语句的id,执行后打印影响的行数。
 * 
 * @author cgnb_liuxb
 *
 */
@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }),
		@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
				RowBounds.class, ResultHandler.class }),
		@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
				RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class }) })
public class AssertResultInterceptor implements Interceptor {
	final Logger logger = LogManager.getLogger(AssertResultInterceptor.class);

	final static Map<String, ExceptRows> STMT_RESULT = new HashMap<String, ExceptRows>();

	boolean init;

	public synchronized void initStmtMapping(MapperRegistry mapperRegistry) {
		if (init) {
			return;
		}

		for (Class<?> mapperInterface : mapperRegistry.getMappers()) {
			if (!mapperInterface.isInterface()) {
				continue;
			}
			for (Method method : mapperInterface.getDeclaredMethods()) {
				ExceptRows exceptRows = null;
				if (method.isAnnotationPresent(UpdateOne.class)) {
					exceptRows = new ExceptRows(1,1);
				} else if (method.isAnnotationPresent(UpdateRows.class)) {
					UpdateRows updateRows = method.getAnnotation(UpdateRows.class);
					exceptRows = new ExceptRows(updateRows.min(),updateRows.max());
				}

				if (exceptRows != null) {
					STMT_RESULT.put(method.getName(), exceptRows);
				}
			}
		}
	}

	static class ExceptRows {
		Integer min;
		Integer max;
		
		public ExceptRows(Integer min, Integer max) {
			super();
			this.min = min;
			this.max = max;
		}

		@Override
		public String toString() {
			if(min==max){
				return String.valueOf(min);
			}
			return "[最小"+min+"最大"+max+"]";
		}
	}

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		MappedStatement st = (MappedStatement) invocation.getArgs()[0];
		if (!init) {
			initStmtMapping(st.getConfiguration().getMapperRegistry());
		}

		Object rs = invocation.proceed();
		if (st.getSqlCommandType() != SqlCommandType.INSERT || st.getSqlCommandType() != SqlCommandType.UPDATE
				|| st.getSqlCommandType() != SqlCommandType.DELETE) {
			return rs;
		}

		String sqlId = st.getId();
		ExceptRows exceptRows = STMT_RESULT.get(sqlId);
		if(exceptRows==null){
			return rs;
		}
		
		Integer actualUpdatedRows = (Integer) rs;
		if (exceptRows.max < actualUpdatedRows || exceptRows.min > actualUpdatedRows) {
			throw new RuntimeException(sqlId + "更新条数" + actualUpdatedRows + "与预期值" + exceptRows + "不符!");
		}
		return rs;
	}

	@Override
	public Object plugin(Object arg0) {
		return Plugin.wrap(arg0, this);
	}

	@Override
	public void setProperties(Properties arg0) {

	}

}
